﻿using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Threading;
using System.Windows.Media;
using System.Collections.Specialized;
using System.Collections.Generic;

using PasswordProvider.View.Behaviors;
using System.Windows.Controls.Primitives;

namespace PasswordProvider.View
{
    /// <summary>
    /// SitesListViewPage: Code behind for the page that hosts the sites list. 
    /// </summary>
    public sealed partial class SitesListViewPage : Page
    {
        private static List<string> OpenGroups = new List<string>();
        
        public SitesListViewPage()
        {
            InitializeComponent();

            DataContextChanged += new DependencyPropertyChangedEventHandler(SitesListViewPage_DataContextChanged);
            Loaded += new RoutedEventHandler(SitesListViewPage_Loaded);
        }
       
        public static readonly DependencyProperty CurrentGroupNameProperty =
            DependencyProperty.Register("CurrentGroupName", typeof(string), typeof(SitesListViewPage));
        public string CurrentGroupName
        {
            get { return (string)GetValue(CurrentGroupNameProperty); }
            set { SetValue(CurrentGroupNameProperty, value); }
        }
        
        #region Event Handlers

        /// <summary>
        /// When the page loads, set the bindings which are set programatically and expand all groups
        /// which were expanded before this page was navigated away from, if there were any.
        /// </summary>
        void SitesListViewPage_Loaded(object sender, RoutedEventArgs e)
        {
            SetBindings();

            if (OpenGroups.Count > 0)
            {
                // open all open groups.
                foreach (Expander expander in GetExpanders())
                {
                    if (OpenGroups.Contains(expander.Tag as string))
                        expander.IsExpanded = true;
                }
            }
            
            _itemsListBox.Focus();

            // make sure the selected item is in view.
            if (_itemsListBox.SelectedIndex >= 0)
            {
                ListBoxItem selectedItem = _itemsListBox.ItemContainerGenerator.ContainerFromIndex(_itemsListBox.SelectedIndex) as ListBoxItem;
                if (selectedItem != null)
                {
                    selectedItem.BringIntoView();
                }
            }
        }

        /// <summary>
        /// Handle event when the listbox recieves keyboard focus. Need to make it so the selected item
        /// has keyboard focus so the arrow keys will work properly. Don't know why selected item doesn't
        /// have keyboard focus automatically.        
        /// </summary>
        void ListBox_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Down || e.Key == Key.Up || e.Key == Key.Right)
            {
                if (Keyboard.FocusedElement == sender)
                    SetFocusOnSelectedItem(sender as ListBox);
            }
        }

        /// <summary>
        /// When the group collection changes, need to clear the open groups list and the current group name.
        /// </summary>
        void GroupDescriptions_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            OpenGroups.Clear();
            CurrentGroupName = null;
        }
        
        /// <summary>
        /// When the data context changes need to set an event handler for the group descriptions change,
        /// because the items source is data bound.
        /// </summary>
        void SitesListViewPage_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            if (_itemsListBox.ItemsSource != null)
            {
                (_itemsListBox.ItemsSource as ListCollectionView).GroupDescriptions.CollectionChanged +=
                    new NotifyCollectionChangedEventHandler(GroupDescriptions_CollectionChanged);

            }
        }

        /// <summary>
        /// When a group is expanded, add the group name to the open groups list.
        /// </summary>
        private void Group_Expanded(object sender, RoutedEventArgs e)
        {
            Expander expander = sender as Expander;

            string groupName = (string)expander.Tag;
            if(!OpenGroups.Contains(groupName))
                OpenGroups.Add(groupName);
        }

        /// <summary>
        /// When a group is collapsed, remove the group from the open groups list.
        /// </summary>
        private void Group_Collapsed(object sender, RoutedEventArgs e)
        {
            string groupName = (string)(sender as Expander).Tag;
            OpenGroups.Remove(groupName);

        }
        
        /// <summary>
        /// When the group as a whole recieves mouse down, set the current group name.
        /// </summary>
        private void Group_PreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
            FrameworkElement element = sender as FrameworkElement;
            if (element == null)
                throw new InvalidOperationException(sender.GetType().Name);

            CurrentGroupName = (string)element.Tag;

            // if clicked on a group header (which is a toggle button) clear the current selection.
            ToggleButton button = element.GetVisuals().OfType<ToggleButton>().First();
            if (button != null)
            {
                if (button.IsHitTestVisible)
                {
                    _itemsListBox.SelectedItem = null;
                }
            }

        }

        #endregion // Event Handlers

        #region Private Helpers

        private List<Expander> GetExpanders()
        {
            return this.GetVisuals().OfType<Expander>().ToList<Expander>();            
        }

        /// <summary>
        /// Set the focus on the selected item.
        /// </summary>
        private void SetFocusOnSelectedItem(ListBox listBox)
        {
            ListBoxItem selected = listBox.ItemContainerGenerator.ContainerFromItem(_itemsListBox.SelectedItem) as ListBoxItem;
            if (selected != null)
            {
                if (Keyboard.FocusedElement != selected)
                {
                    Keyboard.Focus(selected);
                }
            }
        }

        private void SetBindings()
        {
            Binding groupNameBinding = new Binding("CurrentGroupName");
            groupNameBinding.Source = DataContext;
            groupNameBinding.Mode = BindingMode.OneWayToSource;
            this.SetBinding(CurrentGroupNameProperty, groupNameBinding);
        }


        #endregion // private helpers

    }
    

   
}
